using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using AxeFrog.Composition;

namespace AxeFrog.Web
{
	[Export]
	[PartCreationPolicy(CreationPolicy.NonShared)]
	internal class ControllerFactory
	{
		public Lazy<IController, IControllerMetadata>[] ControllerExports { get; set; }

		[ImportingConstructor]
		public ControllerFactory([ImportMany(RequiredCreationPolicy=CreationPolicy.NonShared)] Lazy<IController, IControllerMetadata>[] controllerExports)
		{
			ControllerExports = controllerExports;
		}
	}

	/// <summary>
	/// Replaces DefaultControllerFactory so that controllers can have their dependencies injected
	/// via the MEF container attached to the current HttpApplication instance.
	/// </summary>
	public class MefControllerFactory : IControllerFactory
	{
		private readonly WebApplication _app;
		private const string ControllerExportEntryName = "controllerExport";

		public MefControllerFactory(WebApplication app)
		{
			_app = app;
		}

		public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
		{
			var exports = _app.Container.Resolve<ControllerFactory>().ControllerExports;
			var controllers = exports.Where(exp => string.Compare(exp.Metadata.Name, controllerName, true) == 0);

			if(requestContext.RouteData.DataTokens.ContainsKey("namespaces"))
			{
				var namespaces = requestContext.RouteData.DataTokens["namespaces"] as string[];
				if(namespaces != null)
					controllers = controllers.Where(exp => namespaces.Contains(exp.Metadata.ControllerType.Namespace));
			}
			var controllerExport = controllers.FirstOrDefault();

			if(controllerExport == null)
			{
#if DEBUG
				if(Debugger.IsAttached)
				{
					string loadedAssemblies = _app.LoadedAssemblies;
					string compositionDump = _app.Container.Dump();
					Debugger.Break();
				}
#endif
				throw new HttpException(404, "Page Not Found");
			}

			requestContext.HttpContext.Items[ControllerExportEntryName] = controllerExport;

			return controllerExport.Value;
		}

		public void ReleaseController(IController controller)
		{
			var export = HttpContext.Current.Items[ControllerExportEntryName] as Lazy<IController>;
			if (export != null)
				_app.Container.Release(export);
		}

		private static IEnumerable<string> GetNamespaceFromRoute(System.Web.Routing.RequestContext requestContext)
		{
			object routeNamespacesObj;

			if (requestContext != null && 
			    requestContext.RouteData.DataTokens.TryGetValue("Namespaces", out routeNamespacesObj))
			{
				return routeNamespacesObj as IEnumerable<string>;
			}

			return null;
		}
	}
}